//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using System.Xml.Serialization;
using LargoCommon.Interfaces;
using LargoCommon.Localization;
namespace LargoCommon.Music
{
/// Melodic fragment.
/// Melodic fragment is a subclass of figural structure. Two types of figures are distinguished:
/// modal (tones step within current harmonic modality) and harmonic (tones in actual harmonic structure).
/// Melodic fragment is defined by its pattern. In addition to that some characteristics
/// (like variability, stability or homogeneity...) are planned to be computed.
[Serializable]
[XmlRoot]
public sealed class MelodicStructure : FiguralSchema, IModalStruct
{
#region Constructors
///
/// Initializes a new instance of the MelodicStructure class. Serializable.
public MelodicStructure() {
this.ToneSchema = this.SymbolStringOfMode(0);
}
///
/// Initializes a new instance of the MelodicStructure class.
///
/// The given system.
/// Structural code.
public MelodicStructure(GeneralSystem givenSystem, string structuralCode)
: base(givenSystem, structuralCode) {
Contract.Requires(givenSystem != null);
//// if (givenSystem == null) { return; }
//// if (givenSystem.Order == 0) { throw new ArgumentException("Order of MelodicStructure not defined."); }
//// HarmonicSystem hs = HarmonicSystem.GetHarmonicSystem(DefaultValue.HarmonicOrder);
//// this.HarmonicModality = HarmonicModality.GetNewHarmonicModality(hs, 2741);
}
///
/// Initializes a new instance of the MelodicStructure class.
///
/// The given system.
/// Number of instance.
public MelodicStructure(GeneralSystem givenSystem, decimal number)
: base(givenSystem, number) {
Contract.Requires(givenSystem != null);
}
/// Initializes a new instance of the MelodicStructure class.
/// Figural structure.
public MelodicStructure(FiguralStructure structure)
: base(structure) {
Contract.Requires(structure != null);
}
#endregion
#region Interface - simple properties
/// Gets or sets the starting drift of the structure.
/// Property description.
public int Drift { get; set; }
/// Gets or sets the starting octave of the structure.
/// Property description.
public MusicalOctave Octave { get; set; }
/// Gets or sets the starting band type of the structure.
/// Property description.
public MusicalBand BandType { get; set; }
/// Gets or sets the starting guessed part type of the structure.
/// Property description.
public MelodicFunction MelodicFunction { get; set; }
///
/// Gets or sets the bar number.
///
///
/// The bar number.
///
public int BarNumber { get; set; }
#endregion
#region Interface - object properties
/// Gets harmonic system.
/// Property description.
[XmlIgnore]
public MelodicSystem MelodicSystem => (MelodicSystem)this.GSystem;
/// Gets or sets harmonic modality.
/// Property description.
public HarmonicModality HarmonicModality {
get => (HarmonicModality)this.Modality;
set => this.Modality = value;
}
/// Gets schema of tones.
/// Property description.
[XmlAttribute]
public string ToneSchema { get; private set; }
/// Gets direction.
/// Property description.
public float Direction { get; private set; }
#endregion
#region Properties
///
/// Gets MelodicTypeString.
///
/// Property description.
public string MelodicTypeString => LocalizedMusic.String("MelodicFunction" + ((byte)this.MelodicFunction).ToString(CultureInfo.CurrentCulture));
///
/// Gets BandTypeString.
///
/// Property description.
public string OctaveString => LocalizedMusic.String("Octave" + ((byte)this.Octave).ToString(CultureInfo.CurrentCulture));
#endregion
#region Static factory methods
///
/// Get New Melodic Struct.
///
/// The given system.
/// Structural code.
///
/// Returns value.
///
public static MelodicStructure GetNewMelStruct(GeneralSystem givenSystem, string structuralCode) {
Contract.Requires(givenSystem != null);
var ms = new MelodicStructure(givenSystem, structuralCode);
ms.DetermineBehavior();
return ms;
}
#endregion
#region Comparison
/// Support sorting according to level and number.
/// Object to be compared.
/// Returns value.
public override int CompareTo(object obj) {
return obj is MelodicStructure ms ? string.Compare(this.PositiveElementSchema, ms.PositiveElementSchema, StringComparison.Ordinal) : 0;
//// This kills the DataGrid
//// throw new ArgumentException("Object is not a MelodicStructure");
}
/// Test of equality.
/// Object to be compared.
/// Returns value.
public override bool Equals(object obj) {
//// check null (this pointer is never null in C# methods)
if (object.ReferenceEquals(obj, null)) {
return false;
}
if (object.ReferenceEquals(this, obj)) {
return true;
}
if (this.GetType() != obj.GetType()) {
return false;
}
return this.CompareTo(obj) == 0;
}
#endregion
#region Public methods
///
/// Get Hash Code.
///
/// Returns value.
public override int GetHashCode() {
const byte hashBase = 17;
const byte hashQuotient = 23;
unchecked {
int result = hashBase;
result = (result * hashQuotient) + (this.ToneSchema != null ? this.ToneSchema.GetHashCode() : 0);
result = (result * hashQuotient) + this.Drift.GetHashCode();
result = (result * hashQuotient) + this.Octave.GetHashCode();
result = (result * hashQuotient) + this.BandType.GetHashCode();
result = (result * hashQuotient) + this.MelodicFunction.GetHashCode();
result = (result * hashQuotient) + this.Direction.GetHashCode();
return result;
}
}
/// Evaluate properties of the structure. Used in descendant objects.
/// Must be virtual, because of call from StructuralVariety.
public override void DetermineBehavior() {
this.SetElementsFromDifferences();
}
/// Test of emptiness.
/// Returns value.
public override bool IsEmptyStruct() {
return false;
}
/// Validity test.
/// Returns value.
public override bool IsValidStruct() {
if (this.ElementList.Count <= 0) {
return true;
}
int sum = this.ElementList[this.ElementList.Count - 1];
Contract.Assume(sum > short.MinValue);
//// Only fragments not exceeding 10 modality steps (c. 1-2 octaves)
var ok = Math.Abs(sum) <= 10;
return ok;
}
/// Determine and sets the direction property.
public void ComputeDirection() {
Contract.Requires(this.ElementList.Count > 0);
short firstElem = (byte)this.ElementList[0];
short lastElem = (byte)this.ElementList[this.ElementList.Count - 1];
this.Direction = lastElem - firstElem;
}
///
/// Complete FromElements.
///
public override void CompleteFromElements() {
//// Contract.Requires(0 < this.ElementList.Count);
if (this.ElementList.Count > 0) {
base.CompleteFromElements();
}
if (this.ElementList.Count > 0) {
this.ComputeDirection();
}
}
///
/// Planned altitude.
///
/// The harmonic system.
/// The harmonic modality.
/// Index of the modality.
/// Returns value.
public int PlannedAltitude(GeneralSystem harmonicSystem, BinarySchema harmonicModality, int modalityIndex) {
var elements = this.ElementList;
if (harmonicSystem == null) {
return 0;
}
if (harmonicModality == null) {
return 0;
}
if (elements.Count <= 0) {
return 0;
}
while (modalityIndex < 0) {
modalityIndex += elements.Count;
}
int index = elements[modalityIndex % elements.Count];
int octaveShift = modalityIndex / harmonicModality.Level;
var altitude = (byte)(this.Octave + octaveShift) * harmonicSystem.Order;
if (harmonicModality.Level <= 0) {
return altitude;
}
index = index % harmonicModality.Level;
if (index >= 0) {
altitude += harmonicModality.Places[index];
}
return altitude;
}
#endregion
#region String representation
///
/// Make string of musical symbols.
///
/// Given Shift.
/// Returns value.
public string SymbolStringOfMode(short givenShift) {
var str = new StringBuilder();
var lastE = (byte)(this.GSystem.Order - 1);
var hm = (HarmonicModality)this.Modality;
if (hm == null) {
return string.Empty;
}
string s; //// = hm.Symbol(givenShift);
//// str.Append(s.PadRight(2));
short level;
for (byte e = 0; e < lastE; e++) {
if (e >= this.ElementList.Count) {
continue;
}
level = (short)(this.ElementList[e] + givenShift);
s = hm.SymbolAtLevel(level);
if (s != null) {
str.Append(s.PadRight(2));
}
}
if (lastE >= this.ElementList.Count) {
return str.ToString();
}
level = (short)(this.ElementList[lastE] + givenShift);
s = hm.SymbolAtLevel(level);
if (s != null) {
str.Append(s.PadRight(2));
}
return str.ToString();
}
/// String representation of the object.
/// Returns value.
public override string ToString() {
// s.Append(this.ElementString());
return string.Format(CultureInfo.InvariantCulture, "{0} {1}", base.ToString(), this.ToneSchema);
}
#endregion
#region Transformations
///
/// Makes the descending.
///
public void MakeDescending() {
var cnt = this.ElementList.Count;
for (byte e = 0; e < cnt; e++) {
this.ElementList[e] = (short)(cnt - e);
}
}
///
/// Makes the ascending.
///
public void MakeAscending() {
var cnt = this.ElementList.Count;
for (byte e = 0; e < cnt; e++) {
this.ElementList[e] = e;
}
}
///
/// Inverts this instance.
///
public void Invert() {
var cnt = this.ElementList.Count;
for (byte e = 0; e < cnt; e++) {
this.ElementList[e] = (short)-this.ElementList[e];
}
}
///
/// Magnifies this instance.
///
public void Magnify() {
var cnt = this.ElementList.Count;
for (byte e = 0; e < cnt; e++) {
this.ElementList[e] = (short)(2 * this.ElementList[e]);
}
}
#endregion
}
}